/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

// Grid.cpp : implementation file

#include "stdafx.h"
#include "Grid.h"
#ifdef UNDER_CE
#include <malloc.h>
#endif

#define color_White RGB(255,255,255)
#define color_Black RGB(0,0,0)
#define color_LightGray RGB(192,192,192)
#define color_DarkGray RGB(128,128,128)
#define color_Red RGB(255,0,0)
#define color_Blue RGB(0,0,255)

#define LESSER_OF(a,b) ((a)<(b)?(a):(b))
#define GREATER_OF(a,b) ((a)>(b)?(a):(b))
#define WITHIN(a,b,c) ( ((a)<(c))?\
						( ((a)<=(b)) && ((b)<=(c)) ):\
						( ((a)>=(b)) && ((b)>=(c)) ) )
#define ABS(x) ((x>=0)?(x):(-(x)))

#define EDITING Editing()
BOOL CGrid::Editing()
{
	if (m_bEditLabels) {
		return (m_iEditRow >= -1)&&(m_iEditCol >= -1);
	}
	else {
		return (m_iEditRow >= 0)&&(m_iEditCol >= 0);
	}
}

/////////////////////////////////////////////////////////////////////////////
// CGrid

//CPen c_WhitePen(PS_SOLID,1,color_White);
//CPen c_DotPen(PS_DOT,1,color_Black);

BEGIN_MESSAGE_MAP(CGridEdit, CEdit)
	//{{AFX_MSG_MAP(CGridEdit)
	ON_WM_KILLFOCUS()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CGridEdit::OnKillFocus(CWnd* pNewWnd)
{
	ShowWindow(SW_HIDE);
	CGrid* grid = (CGrid *) GetParent();
	CString text;
	GetWindowText(text);
	grid->Set(grid->m_iEditRow,grid->m_iEditCol,text);
	grid->m_iEditRow = grid->m_iEditCol = -1;
	grid->Invalidate();
	CEdit::OnKillFocus(pNewWnd);
}

CGrid::CGrid()
{
	// initialize all member variables
	m_ID = _T("");
	m_pCell = NULL;
	m_pRowLabel = NULL;
	m_pColLabel = NULL;
	m_nRows = 0;
	m_nCols = 0;
	m_iRowHeight = 0;
	m_iColWidth = NULL;
	m_iLabelRowHeight = 0;
	m_iLabelColWidth = 0;
	m_pLabelFont = NULL;
	m_pCellFont = NULL;
	m_iCellFontHeight = 0;
	m_iTopRow = 0;
	m_iLeftCol = 0;
	m_iRow = m_iRowSel = m_iEditRow = -1;
	m_iCol = m_iColSel = m_iEditCol = -1;
	m_iAutoScroll = TRUE;
	m_iAutoFocus = FALSE;
	m_bEditLabels = false;
}

void CGrid::Create(CWnd* pParent, const RECT & rect,
		int nrows, int ncols,
		int rowHeight,
		int colWidth[],
		int labelRowHeight,
		int labelColWidth,
		CFont* cellFont,
		CFont* labelFont)
{
	m_nRows = nrows;
	m_nCols = ncols;
	m_iRowHeight = rowHeight <= 0 ? 1 : rowHeight;
#ifdef UNDER_CE
	m_iColWidth = (int *) malloc(m_nCols * sizeof(int));
#else
	m_iColWidth = (int *) calloc(m_nCols,sizeof(int));
#endif
	for (int k = 0; k < m_nCols; k++)
		m_iColWidth[k] = colWidth[k];
	m_iLabelRowHeight = labelRowHeight <= 0 ? 1 : labelRowHeight;
	m_iLabelColWidth = labelColWidth <= 0 ? 1 : labelColWidth;
	m_pCellFont = cellFont;
	m_pLabelFont = labelFont;

	RECT myRect = rect;
	//myRect.bottom -= 1;

	CWnd::Create(
		NULL,						//LPCTSTR lpszClassName,
		NULL,						//LPCTSTR lpszWindowName,
		WS_CHILD |
		WS_BORDER |
		WS_TABSTOP |
		WS_VISIBLE,					//DWORD dwStyle,
		myRect,						//const RECT& rect,
		pParent,					//CWnd* pParentWnd,
		0							//UINT nID
		);

	m_Edit.Create(
		WS_CHILD |
		ES_AUTOHSCROLL |
		ES_LEFT |
		ES_WANTRETURN,				//DWORD dwStyle, note NOT VISIBLE
		myRect,						//const RECT& rect,
		this,						//CWnd* pParentWnd,
		0							//UINT nID );
		);

	if (m_pCellFont)
	{
		m_Edit.SetFont(m_pCellFont);
	}

	CFont* font = m_Edit.GetFont();

	LOGFONT logfont;
	font->GetLogFont(&logfont);
	m_iCellFontHeight = ABS(logfont.lfHeight);
}

CGrid::~CGrid()
{
	free(m_iColWidth);
}

// Remove messages that don't exist in CE
#ifdef UNDER_CE
#	define	ON_WM_RBUTTONDOWN()
#	define	ON_WM_RBUTTONUP()
#endif
BEGIN_MESSAGE_MAP(CGrid, CWnd)
	//{{AFX_MSG_MAP(CGrid)
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_HSCROLL()
	ON_WM_VSCROLL()
	ON_WM_LBUTTONDOWN()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_LBUTTONUP()
	ON_WM_SETFOCUS()
	ON_WM_KILLFOCUS()
	ON_WM_MOUSEMOVE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CGrid message handlers

void CGrid::SetNumberRows(int nrows)
{
	m_nRows = nrows;
	if (m_iRow > m_nRows - 1)
	{
		m_iRow = m_nRows - 1;
	}
	if (m_iTopRow > m_nRows - 1)
	{
		m_iTopRow = m_nRows - 1;
	}
	RECT rect;
	GetClientRect(&rect);
	int cy = rect.bottom - rect.top;
	if ((m_iLabelRowHeight + m_nRows * m_iRowHeight) > cy + 1)
	{
		SetScrollRange(SB_VERT,0,m_nRows - 1,FALSE);
		SetScrollPos(SB_VERT,m_iTopRow,FALSE);
	}
	else
	{
		m_iTopRow = 0;
		SetScrollRange(SB_VERT,0,0,FALSE);
	}
	Invalidate();
}

void CGrid::SetTopLeftCell(int row, int col)
{
	m_iTopRow = row;
	m_iLeftCol = col;
	SetScrollPos(SB_VERT,m_iTopRow,TRUE);
	SetScrollPos(SB_HORZ,m_iLeftCol,TRUE);
	Invalidate();
}

void CGrid::SetAutoScroll(int mode)
{
	m_iAutoScroll = mode;
}

void CGrid::SetAutoFocus(int mode)
{
	m_iAutoFocus = mode;
}

/////////////////////////////////////////////////////////////

void CGrid::AutoScroll()
{
	if (!m_iAutoScroll) return;
	while ((m_iRow < m_iTopRow) && (m_iTopRow > 0))
	{
		m_iTopRow--;
		SetScrollPos(SB_VERT,m_iTopRow,TRUE);
	}
	while ((m_iCol < m_iLeftCol) && (m_iLeftCol > 0))
	{
		m_iLeftCol--;
		SetScrollPos(SB_HORZ,m_iLeftCol,TRUE);
	}
	RECT gridrect,cellrect;
	GetClientRect(&gridrect);

	GetCellRect(m_iRow,m_iCol,&cellrect);
	while ((cellrect.bottom > gridrect.bottom) &&
		   (m_iTopRow < m_nRows - 1))
	{
		m_iTopRow++;
		SetScrollPos(SB_VERT,m_iTopRow,TRUE);
		GetCellRect(m_iRow,m_iCol,&cellrect);
	}
	while ((cellrect.right > gridrect.right) &&
		   (m_iLeftCol < m_nCols - 1))
	{
		m_iLeftCol++;
		SetScrollPos(SB_HORZ,m_iLeftCol,TRUE);
		GetCellRect(m_iRow,m_iCol,&cellrect);
	}
}

/////////////////////////////////////////////////////////////
void CGrid::SetSelection(int row, int col, int rowsel, int colsel)
{
	//m_iRow    = LESSER_OF(row,rowsel);
	//m_iRowSel = GREATER_OF(row,rowsel);
	//m_iCol    = LESSER_OF(col,colsel);
	//m_iColSel = GREATER_OF(col,colsel);
	Edit(FALSE);
	if ((m_iRow != row) ||
		(m_iCol != col) ||
		(m_iRowSel != rowsel) ||
		(m_iColSel != colsel))
	{
		OnSelect(&row,&col,&rowsel,&colsel);
	}
	if ((m_iRow != row) ||
		(m_iCol != col) ||
		(m_iRowSel != rowsel) ||
		(m_iColSel != colsel))
	{
		m_iRow = row; m_iCol = col; m_iRowSel = rowsel; m_iColSel = colsel;
		if ((m_iRow != m_iRowSel) || (m_iCol != m_iColSel))
			m_iLargeSelection = TRUE;
		AutoScroll();
		Invalidate();
	}
	OnSelChange();
}

void CGrid::SetSelection(int row, int col)
{
	SetSelection(row,col,row,col);
}

void CGrid::GetSelection(int *row, int *col)
{
	*row = m_iRow;
	*col = m_iCol;
}

void CGrid::GetSelection(int *row, int *col, int *rowsel, int *colsel)
{
	*row = m_iRow; *rowsel = m_iRowSel;
	*col = m_iCol; *colsel = m_iColSel;
}

void CGrid::OnSelect(int *row, int* col, int* rowsel, int* colsel)
{
	//replace with any selection restrictions you like
}

void CGrid::Set(int row, int col, CString text)
{

}

CString CGrid::Get(int row, int col)
{
	CString x;
	x.Format(_T("%d,%d"),row,col);
	return x;
}

#define STYLE (DT_SINGLELINE | DT_NOCLIP | DT_NOPREFIX)
#define CELL_STYLE  (STYLE | DT_LEFT | DT_BOTTOM)
#define LABEL_STYLE (STYLE | DT_CENTER | DT_VCENTER)

#define DrawLabel(l,t,r,b,text)\
	{\
		RECT temp = {l,t,r,b};\
		dc.FillSolidRect(&temp,color_LightGray);\
		dc.DrawText(text,&temp,LABEL_STYLE);\
		dc.Draw3dRect(&temp,color_White,color_DarkGray);\
	}

#define DrawLabelBlank(l,t,r,b)\
	{\
		RECT temp = {l,t,r,b};\
		dc.FillSolidRect(&temp,color_LightGray);\
	}

#define DRAW_TEXT(temp,text)\
	{\
		temp.left += 3;\
		/*temp.bottom--;*/\
		dc.DrawText(text,&temp,CELL_STYLE);\
		temp.left -= 3;\
		/*temp.bottom++;*/\
	}

#define DrawCell(l,t,r,b,text)\
	{\
		RECT temp = {l,t,r,b};\
		dc.FillSolidRect(&temp,color_White);\
		DRAW_TEXT(temp,text);\
		dc.Draw3dRect(&temp,color_White,color_DarkGray);\
	}

#define DrawCurrent(l,t,r,b,text)\
	{\
		RECT temp = {l,t,r,b};\
		dc.FillSolidRect(&temp,color_White);\
		DRAW_TEXT(temp,text);\
		dc.Draw3dRect(&temp,color_White,color_DarkGray);\
		temp.left--;\
		temp.top--;\
		temp.right--;\
		temp.bottom--;\
		dc.DrawFocusRect(&temp);\
	}

#define DrawSelection(l,t,r,b,text)\
	{\
		RECT temp = {l,t,r,b};\
		dc.FillSolidRect(&temp,haveFocus?color_Blue:color_DarkGray);\
		dc.SetTextColor(color_White);\
		DRAW_TEXT(temp,text);\
		dc.SetTextColor(color_Black);\
	}

#define DrawCellBlank(l,t,r,b)\
	{\
		RECT temp = {l,t,r,b};\
		dc.FillSolidRect(&temp,color_White);\
	}

void CGrid::OnPaint()
{
	CPaintDC dc(this); // device context for painting
    CRect rect;
	CRect temp;
	CPen  *savePen = NULL;
	CFont* saveFont = NULL;
    GetClientRect( &rect );
	int top,left;
	CWnd *currentFocus = GetFocus();
	int haveFocus = (currentFocus == this) || (currentFocus == &m_Edit);

	// eventually remove this call
	//dc.FillSolidRect(&rect,color_Red);

	// select the label font
	if (m_pLabelFont) saveFont = dc.SelectObject(m_pLabelFont);

	// draw the label column/row intersection if needed
	if ((m_iLabelColWidth > 0) && (m_iLabelRowHeight > 0))
	{
		DrawLabel(0,0,m_iLabelColWidth,m_iLabelRowHeight,_T(""));
	}

	// draw the label row if needed
	if (m_iLabelRowHeight > 0)
	{
		left = m_iLabelColWidth;
		for (int i = m_iLeftCol; i < m_nCols; i++)
		{
			if (left > rect.right) break;
			int right = left + m_iColWidth[i];
			DrawLabel(left,0,right,m_iLabelRowHeight,Get(-1,i));
			left += m_iColWidth[i];
		}
		if (left < rect.right)
		{
			DrawLabelBlank(left,0,rect.right,m_iLabelRowHeight);
		}
	}

	// draw the label column if needed
	if (m_iLabelColWidth > 0)
	{
		top = m_iLabelRowHeight;
		for (int i = m_iTopRow; i < m_nRows; i++)
		{
			int bottom = top + m_iRowHeight;
			DrawLabel(0,top,m_iLabelColWidth,bottom,Get(i,-1));
			top += m_iRowHeight;
		}
		if (top < rect.bottom)
		{
			DrawLabelBlank(0,top,m_iLabelColWidth,rect.bottom);
		}
	}

	// select the cell font or restore the default font
	if (m_pCellFont)
	{
		dc.SelectObject(m_pCellFont);
	}
	else if (saveFont) // old font was saved when selected labels
	{
		dc.SelectObject(saveFont);
	}

	RECT rc;
	GetWindowRect(&rc);
	int rowsPerScreen = ((rc.bottom - rc.top) / m_iRowHeight) + 1;
	int bottomRow = m_iTopRow + rowsPerScreen - 1;
	if (bottomRow >= m_nRows) bottomRow = m_nRows - 1;

	top = m_iLabelRowHeight;
	for (int k = m_iTopRow; k <= bottomRow; k++)
	{
		int left = m_iLabelColWidth;
		for (int i = m_iLeftCol; i < m_nCols; i++)
		{
			if (left > rect.right) break;
			int right = left + m_iColWidth[i];
			//if ((m_iRow <= k) && (k <= m_iRowSel) &&
			//	(m_iCol <= i) && (i <= m_iColSel))
			if (WITHIN(m_iRow,k,m_iRowSel) && WITHIN(m_iCol,i,m_iColSel))
			{
				if ((k == m_iRow) && (i == m_iCol) && (k == m_iRowSel) && (i==m_iColSel))
				{
					DrawCurrent(left,top,right,top+m_iRowHeight,Get(k,i));
				}
				else
				{
					DrawSelection(left,top,right,top+m_iRowHeight,Get(k,i));
				}
			}
			else
			{
				DrawCell(left,top,right,top+m_iRowHeight,Get(k,i));
			}
			left += m_iColWidth[i];
		}
		if (left < rect.right)
		{
			DrawCellBlank(left,top,rect.right,top + m_iRowHeight);
		}
		top += m_iRowHeight;
	}
	if (top < rect.bottom)
	{
		DrawCellBlank(m_iLabelColWidth,top,rect.right,rect.bottom);
	}

	// draw the grid graphics
	//if (GetFocus() == this)
	if (haveFocus)
	{
		GetClientRect( &rect );
		rect.right = rect.left + 2;
		dc.FillSolidRect(&rect,color_Blue);
		//GetClientRect( &rect );
		//rect.left = rect.right + 2;
		//dc.FillSolidRect(&rect,color_Blue);

		//dc.MoveTo(rect.left,rect.top);
		//dc.LineTo(rect.right,rect.bottom);
	}
}

void CGrid::OnSize(UINT nType, int cx, int cy)
{
	CWnd::OnSize(nType, cx, cy);
	if ((m_iLabelRowHeight + m_nRows * m_iRowHeight) > cy + 1) //(rect.bottom - rect.height))
	{
		SetScrollRange(SB_VERT,0,m_nRows - 1,FALSE);
		SetScrollPos(SB_VERT,m_iTopRow,FALSE);
	}
	else
	{
		m_iTopRow = 0;
		SetScrollRange(SB_VERT,0,0,FALSE);
	}
	int sum = 0;
	for (int k = 0; k < m_nCols; k++) sum += m_iColWidth[k];
	if ((m_iLabelColWidth + sum) > cx) //(rect.right - rect.left))
	{
		SetScrollRange(SB_HORZ,0,m_nCols - 1,FALSE);
		SetScrollPos(SB_HORZ,m_iLeftCol,TRUE);
	}
	else
	{
		m_iLeftCol = 0;
		SetScrollRange(SB_HORZ,0,0,TRUE);
	}
}

void CGrid::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	RECT rect;
	int width; // width of the cell client area
	int sum;

	Edit(FALSE);

	// TODO: Add your message handler code here and/or call default
	switch (nSBCode)
	{
	case SB_LINELEFT:
		if (m_iLeftCol > 0)
			SetScrollPos(SB_HORZ,--m_iLeftCol,TRUE);
			Invalidate();
		break;
	case SB_LINERIGHT:
		if (m_iLeftCol < (m_nCols-1))
			SetScrollPos(SB_HORZ,++m_iLeftCol,TRUE);
			Invalidate();
		break;
	case SB_PAGELEFT:
		if (m_iLeftCol > 0)
		{
			GetClientRect(&rect);
			width = (rect.right - rect.left - m_iLabelColWidth);
			sum = 0;
			for (int k = m_iLeftCol; k > 0; k--)
			{
				sum = sum + m_iColWidth[k];
				if (sum > width)
				{
					if (k == m_iLeftCol) k--;
					SetScrollPos(SB_HORZ,m_iLeftCol = k,TRUE);
					Invalidate();
					break;
				}
			}
			if (sum <= width)
			{
				SetScrollPos(SB_HORZ,m_iLeftCol = 0,TRUE);
				Invalidate();
			}
		}
		break;
	case SB_PAGERIGHT:
		if (m_iLeftCol < (m_nCols-1))
		{
			GetClientRect(&rect);
			width = (rect.right - rect.left - m_iLabelColWidth);
			sum = 0;
			for (int k = m_iLeftCol; k < m_nCols; k++)
			{
				sum = sum + m_iColWidth[k];
				if (sum > width)
				{
					if (k == m_iLeftCol) k++;
					SetScrollPos(SB_HORZ,m_iLeftCol = k,TRUE);
					Invalidate();
					break;
				}
			}
			if (sum <= width)
			{
				SetScrollPos(SB_HORZ,m_iLeftCol = m_nCols - 1,TRUE);
				Invalidate();
			}
		}
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		SetScrollPos(SB_HORZ,m_iLeftCol = nPos,TRUE);
		Invalidate();
		break;
	case SB_LEFT:
		SetScrollPos(SB_HORZ,m_iLeftCol = m_nCols - 1,TRUE);
		Invalidate();
		break;
	case SB_RIGHT:
		SetScrollPos(SB_HORZ,m_iLeftCol = 0,TRUE);
		Invalidate();
		break;
	}
	CWnd::OnHScroll(nSBCode, nPos, pScrollBar);
}

void CGrid::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
	RECT rect;
	int visibleRows;

	Edit(FALSE);

	// TODO: Add your message handler code here and/or call default
	switch (nSBCode)
	{
	case SB_LINEDOWN:
		if (m_iTopRow < (m_nRows-1))
		{
			SetScrollPos(SB_VERT,++m_iTopRow,TRUE);
			Invalidate();
		}
		break;
	case SB_LINEUP:
		if (m_iTopRow > 0)
		{
			SetScrollPos(SB_VERT,--m_iTopRow,TRUE);
			Invalidate();
		}
		break;
	case SB_PAGEDOWN:
		if (m_iTopRow < (m_nRows-1))
		{
			GetClientRect(&rect);
			visibleRows = (rect.bottom - rect.top - m_iLabelRowHeight) / m_iRowHeight;
			m_iTopRow += visibleRows - 1;
			if (m_iTopRow > m_nRows-1) m_iTopRow = m_nRows - 1;
			SetScrollPos(SB_VERT,m_iTopRow,TRUE);
			Invalidate();
		}
		break;
	case SB_PAGEUP:
		if (m_iTopRow > 0)
		{
			GetClientRect(&rect);
			visibleRows = (rect.bottom - rect.top - m_iLabelRowHeight) / m_iRowHeight;
			m_iTopRow -= visibleRows - 1;
			if (m_iTopRow < 0) m_iTopRow = 0;
			SetScrollPos(SB_VERT,m_iTopRow,TRUE);
			Invalidate();
		}
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		SetScrollPos(SB_VERT,m_iTopRow = nPos,TRUE);
		Invalidate();
		break;
	case SB_TOP:
		SetScrollPos(SB_VERT,m_iTopRow = 0,TRUE);
		Invalidate();
		break;
	case SB_BOTTOM:
		SetScrollPos(SB_VERT,m_iTopRow = m_nRows - 1,TRUE);
		Invalidate();
		break;
	}
	CWnd::OnVScroll(nSBCode, nPos, pScrollBar);
}

void CGrid::OnLButtonDown(UINT nFlags, CPoint point)
{
	int row,col;
	GetCell(point,&row,&col);
	OnClick(row,col,GRID_LEFT_BUTTON,GRID_BUTTON_DOWN);
	CWnd::OnLButtonDown(nFlags, point);
}

void CGrid::OnLButtonUp(UINT nFlags, CPoint point)
{
	int row,col;
	GetCell(point,&row,&col);
	OnClick(row,col,GRID_LEFT_BUTTON,GRID_BUTTON_UP);
//	if (IsWindow(this->hWnd))
		CWnd::OnLButtonUp(nFlags, point);
}

#ifndef UNDER_CE
void CGrid::OnRButtonDown(UINT nFlags, CPoint point)
{
	int row,col;
	GetCell(point,&row,&col);
	OnClick(row,col,GRID_RIGHT_BUTTON,GRID_BUTTON_DOWN);
	CWnd::OnRButtonDown(nFlags, point);
}

void CGrid::OnRButtonUp(UINT nFlags, CPoint point)
{
	int row,col;
	GetCell(point,&row,&col);
	OnClick(row,col,GRID_RIGHT_BUTTON,GRID_BUTTON_UP);
	CWnd::OnLButtonUp(nFlags, point);
}
#endif

void CGrid::OnSetFocus(CWnd* pOldWnd)
{
	Invalidate();
	if (pOldWnd != &m_Edit)
	{
		SetSelection(0,0);
	}
	CWnd::OnSetFocus(pOldWnd);
}

void CGrid::OnKillFocus(CWnd* pNewWnd)
{
	if (pNewWnd != &m_Edit)
	{
		Edit(FALSE);
	}
	Invalidate();
	CWnd::OnKillFocus(pNewWnd);
}

void CGrid::GetCell(CPoint point, int * row, int * col)
{
	// find the row
	int y = point.y;
	if (y < m_iLabelRowHeight)
	{
		(*row) = -1;
	}
	else
	{
		(*row) = (y - m_iLabelRowHeight) / m_iRowHeight + m_iTopRow;
		if ((*row) > (m_nRows - 1)) *row = INVALID_INDEX;//(*row) = m_nRows - 1;
	}
	// find the column
	int x = point.x;
	if (x < m_iLabelColWidth)
	{
		(*col) = -1;
	}
	else
	{
		(*col) = m_nCols - 1;
		x -= m_iLabelColWidth;
		for (int k = m_iLeftCol; k < m_nCols; k++)
		{
			x -= m_iColWidth[k];
			if (x<=0)
			{
				(*col) = k;
				break;
			}
		}
	}
}

void CGrid::OnClick(int row, int col, int button, int down)
{
	SetFocus();
	if ((row == INVALID_INDEX) || (col == INVALID_INDEX)) {
		return;
	}
	else if (down)
	{
		m_iLargeSelection = FALSE;
		if (col == -1)
			SetSelection(row,col,row,m_nCols-1);
		else if (row == -1)
			SetSelection(row,col,m_nRows-1,col);
		else
			SetSelection(row,col);
	}
	else
	{
		if ((button == GRID_LEFT_BUTTON) && (!m_iLargeSelection) &&
			WITHIN(0,row,m_nRows-1) && (row == m_iRow) && (m_iRow == m_iRowSel) &&
			WITHIN(0,col,m_nCols-1) && (col == m_iCol) && (m_iCol == m_iColSel))
		{
			OnFocus(row,col);
		}
	}
}

int  CGrid::OnKeypress(int row, int col,
					   int key, int shift, int control,
					   int editing)
{
	if (editing)
	{
		int first, last, length;

		m_Edit.GetSel(first,last);
		length = m_Edit.GetWindowTextLength();

		if      (key == VK_RETURN)
		{
			Edit(FALSE); return(TRUE);
		}
		else if (key == VK_ESCAPE)
		{
			if (WITHIN(0,row,m_nRows-1) && WITHIN(0,col,m_nCols-1))
				m_Edit.SetWindowText(Get(m_iRow,m_iCol));
			Edit(FALSE); return(TRUE);
		}
		else if (key == VK_UP)
		{
			if ((m_iRow > 0) && (m_iCol >= 0))
				SetSelection(m_iRow-1,m_iCol);
		}
		else if (key == VK_DOWN)
		{
			if ((m_iRow < m_nRows - 1) && (m_iCol >= 0))
				SetSelection(m_iRow+1,m_iCol);
		}
		else if ((key == VK_LEFT) &&
				 (control||((first==0)&&(last==0))))
		{
			if ((m_iCol > 0) && (m_iRow >= 0))
				SetSelection(m_iRow,m_iCol-1);
		}
		else if ((key == VK_RIGHT) &&
				 (control||((first==length)&&(last==length))))
		{
			if ((m_iCol < m_nCols - 1) && (m_iRow >= 0))
				SetSelection(m_iRow,m_iCol+1);
		}
		else
		{
			return (FALSE);
		}
		if (m_iAutoFocus) OnFocus(m_iRow,m_iCol);
		return (TRUE);
	}
	else
	{
		int tRow = row;
		int tCol = col;
		if      ((key == VK_INSERT) || (key == GRID_VK_SLASH))
		{
			if (WITHIN(0,row,m_nRows-1) && WITHIN(0,col,m_nCols-1) &&
				(m_iRow == m_iRowSel) && (m_iCol == m_iColSel))
				OnEdit(m_iRow,m_iCol);
			return(TRUE);
		}
		else if (key == VK_SPACE)
		{
			if (WITHIN(0,row,m_nRows-1) && WITHIN(0,col,m_nCols-1) &&
				(m_iRow == m_iRowSel) && (m_iCol == m_iColSel))
				OnFocus(m_iRow,m_iCol);
			return(TRUE);
		}
		else if (key == VK_DELETE)
		{
			if (WITHIN(0,row,m_nRows-1) && WITHIN(0,col,m_nCols-1))
				OnDelete(m_iRow,m_iCol,m_iRowSel,m_iColSel);
			return(TRUE);
		}
		else if (key == VK_ESCAPE)
		{
			if (WITHIN(0,row,m_nRows-1) && WITHIN(0,col,m_nCols-1))
				SetSelection(m_iRow,m_iCol);
			return(TRUE);
		}
		else if (key == VK_UP)
		{
			if ((m_iRow > 0) && (m_iCol >= -1)) {
				tRow--;
			}
		}
		else if (key == VK_DOWN)
		{
			if ((m_iRow < m_nRows - 1) && (m_iCol >= -1)) {
				tRow++;
			}
		}
		else if (key == VK_LEFT)
		{
			if ((m_iCol > 0) && (m_iRow >= -1)) {
				tCol--;
			}
		}
		else if (key == VK_RIGHT)
		{
			if ((m_iCol < m_nCols - 1) && (m_iRow >= -1)) {
				tCol++;
			}
		}
		else
		{
			return(FALSE);
		}
		if (shift)
			SetSelection(tRow,tCol,m_iRowSel,m_iColSel);
		else
			SetSelection(tRow,tCol);

		return(TRUE);
	}
}

void CGrid::OnSelChange()
{
	// Virtual
}

void CGrid::OnFocus(int row, int col)
{
	Edit(TRUE);
}

void CGrid::OnEdit(int row, int col)
{
	Edit(TRUE);
}

void CGrid::OnDelete(int row, int col)
{
	Set(row,col,_T(""));
	Invalidate();
}

void CGrid::OnDelete(int row, int col, int rowsel, int colsel)
{
	for (int i=LESSER_OF(row,rowsel); i<=GREATER_OF(row,rowsel); i++)
		for (int j=LESSER_OF(col,colsel); j<=GREATER_OF(col,colsel); j++)
		{
			OnDelete(i,j);
		}
}

void CGrid::GetCellRect(int row, int col, LPRECT rect)
{
	if ((row < m_iTopRow) || (row > m_nRows-1) ||
		(col < m_iLeftCol) || (col > m_nCols-1))
	{
		rect->left = rect->right = rect->top = rect->bottom = 0;
	}
	rect->top = m_iLabelRowHeight + (row - m_iTopRow) * m_iRowHeight;
	rect->bottom = rect->top + m_iRowHeight;
	if (col == -1) {
		rect->left = 2;
		rect->right = m_iLabelColWidth;
	}
	else {
		rect->left = m_iLabelColWidth;
		for (int k = m_iLeftCol; k < col; k++) rect->left += m_iColWidth[k];
		rect->right = rect->left + m_iColWidth[k];
	}
}

void CGrid::OnMouseMove(UINT nFlags, CPoint point)
{
	/*
	MK_CONTROL   Set if the CTRL key is down.
	MK_LBUTTON   Set if the left mouse button is down.
	MK_MBUTTON   Set if the middle mouse button is down.
	MK_RBUTTON   Set if the right mouse button is down.
	MK_SHIFT   Set if the SHIFT key is down.
	*/
	if (nFlags & MK_LBUTTON)
	{
		int row, col;
		GetCell(point,&row,&col);
		//SetSelection(m_iRow,m_iCol,row,col);
		if (row != INVALID_INDEX)
			SetSelection(row,col,m_iRowSel,m_iColSel);
	}

	CWnd::OnMouseMove(nFlags, point);
}

void CGrid::Edit(int mode)
{
	if (mode)
	{
		if (!m_bEditLabels && ((m_iRow < 0)||(m_iCol < 0)))
			return;
		m_iEditRow = m_iRow;
		m_iEditCol = m_iCol;

		RECT rect;
		GetCellRect(m_iRow,m_iCol,&rect);
#if UNDER_CE
		rect.left += 3;
#else
		rect.left += 1;
#endif
		rect.top = rect.bottom - m_iCellFontHeight - 3 ;
		rect.right -= 2;
		rect.bottom -= 2;

		m_Edit.SetWindowText(Get(m_iRow,m_iCol));
		m_Edit.MoveWindow(&rect);
		m_Edit.ShowWindow(SW_SHOW);
		m_Edit.SetSel(0,-1,TRUE);  //no autoscroll
		m_Edit.SetFocus();
		m_Edit.Invalidate();
	}
	else
	{
		if (!m_bEditLabels && ((m_iEditRow < 0)||(m_iEditCol < 0)))
			return;
		if (GetFocus() == &m_Edit)
		{
			SetFocus();	//focus should do all this when edit loses focus
		}
		else
		{
			CString text;
			m_Edit.GetWindowText(text);
			Set(m_iEditRow,m_iEditCol,text);
			m_iEditRow = m_iEditCol = -1;
			m_Edit.ShowWindow(SW_HIDE);
			Invalidate();
		}
	}
}

BOOL CGrid::PreTranslateMessage(MSG* pMsg)
{
	if (pMsg->message == WM_KILLFOCUS)
	{
		Invalidate();
		return (FALSE);
	}
	if (pMsg->message == WM_KEYDOWN)
	{
		int shift   = (GetKeyState(VK_SHIFT)   & 0x800);
		int control = (GetKeyState(VK_CONTROL) & 0x800);
		if (OnKeypress(m_iRow,m_iCol,pMsg->wParam,shift,control,EDITING))
			return (TRUE);
	}
	return CWnd::PreTranslateMessage(pMsg);
}


void CGrid::SetID(CString ID)
{
	m_ID = ID;
}

CString CGrid::GetID()
{
	return m_ID;
}

bool CGrid::SetEditLabels(bool bEnable)
{
	bool bOld = m_bEditLabels;
	m_bEditLabels = bEnable;
	return bOld;
}

bool CGrid::GetEditLabels()
{
	return m_bEditLabels;
}
